1248. 统计「优美子数组」
1248. 统计「优美子数组」
Similar Question
Solution Tips
方案一: 滑动窗口
不断右移 right 指针来扩大滑动窗口,使其包含 k 个奇数;
若当前滑动窗口包含了 k 个奇数,则如下「计算当前窗口的优美子数组个数」:
统计第 1 个奇数左边的偶数个数 leftEvenCnt。 这 leftEvenCnt 个偶数都可以作为「优美子数组」的起点,因此起点的选择有 leftEvenCnt + 1 种(因为可以一个偶数都不取,因此别忘了 +1 喔)。
统计第 k 个奇数右边的偶数个数 rightEvenCnt 。 这 rightEvenCnt 个偶数都可以作为「优美子数组」的终点,因此终点的选择有 rightEvenCnt + 1 种(因为可以一个偶数都不取,因此别忘了 +1 喔)。
因此「优美子数组」左右起点的选择组合数为 (leftEvenCnt + 1) * (rightEvenCnt + 1)
。
var numberOfSubarrays = function(nums, k) {
// 要求的是子数组的数目, 得遍历全部的子数组才行呀, 不然怎么知道呢?
// 滑动窗口, 滑动直至恰好有 k 个奇数, 然后收缩一个, 再继续滑动
// 这题滑动窗口比前缀和更容易想
let res = 0;
let count = 0;
let left = 0;
let right = 0;
while (left <= right && right < nums.length) {
while (count < k && right < nums.length) {
if (nums[right] % 2 === 1) {
count++
}
right++;
}
if (count !== k) {
// right 已经扩张到最大了, 都搜集不满, 之后 left 一收缩就更没有了
return res;
}
// 此刻 count === k
// 继续向右寻找到 right 的边界, 即下一个位置是奇数, 此时是包含这k个奇数的 right 的边界
let rightEvenCount = 0;
while (right < nums.length && (nums[right] % 2) == 0) {
right++;
rightEvenCount++;
}
// 此时 nums[right] 为奇数
let leftEvenCount = 0;
// 收缩 left, 直到不等于 k
while (count === k && left <= right) {
if (nums[left] % 2 === 1) {
count--;
}
else {
leftEvenCount++;
}
left++;
}
res += (leftEvenCount + 1) * (rightEvenCount + 1);
}
return res;
};
方案二: 前缀和
计算前缀和数组 arr:遍历原数组,每遍历一个元素,计算当前的前缀和(即到当前元素为止,数组中有多少个奇数);
对上述前缀和数组,双重循环统计 arr[j] - arr[i] == k
的个数,这样做是 O(N^2) 的(这里会超时哦)。
优化:因此,我们可以像「1. 两数之和」那样使用 HashMap 优化到 O(N)O(N)O(N),键是「前缀和」,值是「前缀和的个数」(下面代码中具体使用的是 int[] prefixCnt 数组,下标是「前缀和」,值是「前缀和的个数」),因此我们可以遍历原数组,每遍历到一个元素,计算当前的前缀和 sum,就在 res 中累加上前缀和为 sum - k 的个数。
var numberOfSubarrays = function(nums, k) {
let befores = [0]
for (let i = 0; i < nums.length; i++) {
// 奇数
if (nums[i] % 2 !== 0) {
befores[i + 1] = befores[i] + 1
} else {
befores[i + 1] = befores[i]
}
}
const map = new Map()
map.set(0, 1)
let cnt = 0
for (let i = 1; i < befores.length; i++) {
const dValue = befores[i] - k
if (map.has(dValue)) {
cnt += map.get(dValue)
}
map.set(befores[i], map.get(befores[i]) ? map.get(befores[i]) + 1 : 1)
}
return cnt
};